home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / Scrolling Panel / ScrollDialog.c next >
Text File  |  1994-07-19  |  15KB  |  570 lines

  1. /***************************************************************************************
  2. *
  3. *        ScrollDialog.c- A useful library for implementing scrollable text boxes in
  4. *                            a dialog. Can be used for elaborate about boxes, etc.
  5. *
  6. *        ©1993 by Graham Cox. All Rights Reserved.
  7. *
  8. *        20/3/93    Modified to allow edit operations if required.
  9. *        10/2/94    Routines added to support SimpleHelp library
  10. *        10/2/94    Dependency on xAlert library removed
  11. *        13/7/94    Converted to Universal Headers and compiled for PowerPC
  12. *
  13. ***************************************************************************************/
  14.  
  15. /*
  16.  
  17. Basic operation. This library requires that you set up a dialog box in a certain way in
  18. order to use it properly. For versatility, both a high level and a low level interface
  19. is provided, for the purposes for which this code was designed, the high level inter-
  20. face is generally the most useful.
  21.  
  22. Putting scrollable styled text into a dialog is a bit of a chore, in that it requires
  23. three tedious custom procedures- a user item proc to draw it, a filter proc to handle
  24. the scroll bar properly, and a control action proc to scroll the text. By bundling these
  25. functions into a high level dialog call, all this is taken care of for you. This library
  26. can be used with most dialog templates, the only prerequisite is that you create a user
  27. item to delineate the area to display the text as part of the dialog template. The
  28. scrollbar is created for you at run time, and is positioned on the right of the useritem.
  29.  
  30. The high level call requires an ID for the template, the item number of the useritem
  31. to use for the text, and the item number of a TEXT resource. The dialog is set up and
  32. displayed, then handled. For this call, a hit in any enabled item will close the dialog
  33. and return, so in this respect it acts like a special alert box. For more control, you
  34. will need to handle hits yourself. The only restriction in this case is that you don't
  35. require a custom filter yourself- as one is already in use, you will need to do every-
  36. thing yourself if you need very complex dialog handling. In this case I would suggest
  37. you use a window anyway.
  38.  
  39. */
  40.  
  41. #include    "ScrollDialog.h"
  42. #include    "ColourPopUp.h"
  43.  
  44. /****************************************************************************************
  45.  
  46. INTERNAL FUNCTIONS
  47. */
  48.  
  49.  
  50.  
  51. pascal void TDUserItem(DialogPtr theDialog,short theItem)
  52. {
  53.     /* draws text in user item using textedit. Dialog's RefCon is handle to text rec */
  54.     
  55.     short            itemType;
  56.     Handle        itemHand;
  57.     Rect        itemBox;
  58.     TDRecHdl    temp;
  59.     
  60.     if (theDialog != NIL)
  61.     {
  62.         GetDItem(theDialog,theItem,&itemType,&itemHand,&itemBox);
  63.         temp = (TDRecHdl) GetWRefCon(theDialog);
  64.         if (temp != NIL)
  65.         {
  66.             //EraseRect(&itemBox);
  67.             TEUpdate(&itemBox,(*temp)->TDText);
  68.         }
  69.         Frame3DRect(&itemBox,FALSE);
  70.         FrameRect(&itemBox);
  71.     }
  72. }
  73.  
  74.  
  75. pascal void TDScrollProc(ControlHandle theControl,short partCode)
  76. {
  77.     /* scrolls text when scroll bar is tracked */
  78.     
  79.     short         oldValue,newValue;
  80.     TDRecHdl    temp;
  81.     TEHandle    theText;
  82.     
  83.     if (theControl != NIL)
  84.     {
  85.         temp = (TDRecHdl) GetCRefCon(theControl);
  86.         oldValue = newValue = GetCtlValue(theControl);
  87.         switch (partCode)
  88.         {
  89.             case inUpButton:
  90.                 newValue -= (*temp)->lineHeight;
  91.                 break;
  92.             case inDownButton:
  93.                 newValue += (*temp)->lineHeight;
  94.                 break;
  95.             case inPageUp:
  96.                 newValue -= (*temp)->pageHeight;
  97.                 break;
  98.             case inPageDown:
  99.                 newValue += (*temp)->pageHeight;
  100.                 break;
  101.             default:
  102.                 break;
  103.         }
  104.         SetCtlValue(theControl,newValue);
  105.         newValue = GetCtlValue(theControl);
  106.         theText = (*temp)->TDText;
  107.         if (theText != NIL)
  108.             TEScroll(0,oldValue - newValue,theText);
  109.     }
  110. }
  111.  
  112.  
  113. pascal Boolean TDFilter(DialogPtr theDialog,EventRecord *theEvent,short *itemHit)
  114. {
  115.     /* this is the filter proc to implement auto scrolling and everything else */
  116.     
  117.     Point                mClick;
  118.     ControlHandle        theControl = NIL;
  119.     short                partCode,itemType,ctlShift;
  120.     char                theKey;
  121.     Handle                itemHand;
  122.     Rect                itemBox;
  123.     long                dTime;
  124.     TEHandle            theText;
  125.     TDRecHdl            temp;
  126.     GrafPtr                savePort;
  127.     Boolean                canWrite;
  128.     CursHandle            theCursor;
  129.     ControlActionUPP    gVActionUPP;
  130.     
  131.     if (theDialog != NIL)
  132.     {
  133.         GetPort(&savePort);
  134.         SetPort(theDialog);
  135.         
  136.         temp = (TDRecHdl) GetWRefCon(theDialog);
  137.         if (temp != NIL)
  138.         {
  139.             theText = (*temp)->TDText;
  140.             
  141.             canWrite = (*temp)->editable;
  142.             if (canWrite)
  143.             {
  144.                 TEIdle(theText);
  145.                 GetMouse(&mClick);
  146.                 if (PtInRect(mClick,&(*theText)->viewRect))
  147.                 {
  148.                     theCursor = GetCursor(iBeamCursor);
  149.                     SetCursor (*theCursor);
  150.                 }
  151.                 else
  152.                     InitCursor();
  153.             }
  154.             switch (theEvent->what)
  155.             {
  156.                 case mouseDown:
  157.                     mClick = theEvent->where;
  158.                     GlobalToLocal(&mClick);
  159.                     
  160.                     partCode = FindControl(mClick,theDialog,&theControl);
  161.                     if ((theControl != NIL) && (theControl == (*temp)->TDControl))
  162.                     {
  163.                         if (partCode == inThumb)
  164.                         {
  165.                             *itemHit = 0;
  166.                             ctlShift = GetCtlValue(theControl);
  167.                             partCode = TrackControl(theControl,mClick,NIL);
  168.                             if (partCode == inThumb) {
  169.                                 ctlShift -= GetCtlValue(theControl);
  170.                                 TEScroll(0,ctlShift,theText);
  171.                             }
  172.                         }
  173.                         else
  174.                         {
  175.                             gVActionUPP = NewControlActionProc((ProcPtr) TDScrollProc);
  176.                             partCode = TrackControl(theControl,mClick,gVActionUPP);
  177.                             DisposeRoutineDescriptor(gVActionUPP);
  178.                             *itemHit = 0;
  179.                         }
  180.                         SetPort(savePort);
  181.                         return(TRUE);
  182.                     }
  183.                     else
  184.                     {
  185.                         /* not our special scrollbar, so just return FALSE, let it be handled
  186.                             by the dialog manager- it doesn't do a lot for its living */
  187.                         
  188.                         if (canWrite)
  189.                         {
  190.                             if (PtInRect(mClick,&(*theText)->viewRect))
  191.                             {
  192.                                 TEClick(mClick,(theEvent->modifiers & shiftKey) == shiftKey,theText);
  193.                                 UpdateTDBar(theText,(*temp)->TDControl);
  194.                             }
  195.                         }
  196.                         SetPort(savePort);
  197.                         return(FALSE);
  198.                     }
  199.                     break;
  200.                 case keyDown:
  201.                 case autoKey:
  202.                     theKey = theEvent->message & charCodeMask;
  203.                     if (canWrite)
  204.                     {
  205.                         /* edit flag TRUE, so pass key presses to text */
  206.                         TEKey(theKey,theText);
  207.                         UpdateTDBar(theText,(*temp)->TDControl);
  208.                     }
  209.                     else
  210.                     {
  211.                         if (theKey == 0x0D || theKey == 0x03)
  212.                         {
  213.                             GetDItem(theDialog,1,&itemType,&itemHand,&itemBox);
  214.                             HiliteControl((ControlHandle)itemHand,1);
  215.                             Delay(8,&dTime);
  216.                             HiliteControl((ControlHandle)itemHand,0);
  217.                             *itemHit = 1;
  218.                             SetPort(savePort);
  219.                             return(TRUE);
  220.                         }
  221.                     }
  222.                     break;
  223.                 default:
  224.                     break;
  225.             }
  226.         }
  227.         SetPort(savePort);
  228.     }
  229.     return(FALSE);
  230. }
  231.  
  232.  
  233. pascal    DialogPtr        GetTextDialog(short dialogID,short textUserItem,short textResourceID,Boolean canWrite)
  234. {
  235.     /* gets a dialog of the required ID, then sets up the given user item as a scrollable
  236.         text item using the text resource passed. If the text resource doesn't exist,
  237.         then you'll need to install the text yourself */
  238.     
  239.     DialogPtr            theDialog = NIL;
  240.     TDRecHdl            temp;
  241.     TEHandle            theText;
  242.     ControlHandle        tdBar;
  243.     short                itemType,tHeight;
  244.     Handle                itemHand,textRes;
  245.     Rect                itemBox,tdBarRect;
  246.     GrafPtr                savePort;
  247.     StScrpHandle        stRec = NIL;
  248.     FontInfo            fInfo;
  249.     short                saveFont,saveSize,saveFace;
  250.     static UserItemUPP    gMyUserItemUPP;
  251.     
  252.     theDialog = GetNewDialog(dialogID,NIL,(WindowPtr) -1L);
  253.     if (theDialog != NIL)
  254.     {
  255.         GetDItem(theDialog,textUserItem,&itemType,&itemHand,&itemBox);
  256.         GetPort(&savePort);
  257.         SetPort(theDialog);
  258.         
  259.         if ((itemType & 0x007F) == userItem)
  260.         {
  261.             /* it really is a user item so set its procedure up */
  262.             temp = (TDRecHdl) NewHandle(sizeof(TDRecord));
  263.             SetWRefCon(theDialog,(long) temp);
  264.             if (temp != NIL)
  265.             {    
  266.                 saveFont = theDialog->txFont;
  267.                 saveSize = theDialog->txSize;
  268.                 saveFace = theDialog->txFace;
  269.                 
  270.                 gMyUserItemUPP = NewUserItemProc((ProcPtr) TDUserItem);
  271.                 itemHand = (Handle) gMyUserItemUPP;
  272.                 SetDItem(theDialog,textUserItem,itemType,itemHand,&itemBox);
  273.                 
  274.                 TextFont(geneva);
  275.                 TextSize(9);
  276.                 
  277.                 GetFontInfo(&fInfo);
  278.                 SetRect(&tdBarRect,0,0,16,itemBox.bottom - itemBox.top);    
  279.                 itemBox.right -= 16;
  280.                 OffsetRect(&tdBarRect,itemBox.right,itemBox.top);
  281.                 InsetRect(&itemBox,3,3);
  282.                 (*temp)->lineHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
  283.                 (*temp)->pageHeight = (itemBox.bottom - itemBox.top) -
  284.                                          (*temp)->lineHeight;
  285.                 (*temp)->editable = canWrite;
  286.                 
  287.                 theText = TEStylNew(&itemBox,&itemBox);
  288.                 tdBar = NewControl(theDialog,&tdBarRect,"\p",TRUE,0,0,100,16,0);
  289.                 
  290.                 (*temp)->TDText = theText;
  291.                 (*temp)->TDControl = tdBar;
  292.                 (*temp)->reserved = 0;
  293.                 
  294.                 /* ready to go- try loading the text */
  295.                 
  296.                 if (theText != NIL)
  297.                 {
  298.                     textRes = GetResource('TEXT',textResourceID);
  299.                     if (textRes != NIL)
  300.                     {
  301.                         stRec = (StScrpHandle) GetResource('styl',textResourceID);
  302.                         
  303.                         HLock(textRes);
  304.                         TEStylInsert(*textRes,GetHandleSize(textRes),stRec,theText);
  305.                         HUnlock(textRes);
  306.                         
  307.                         ReleaseResource(textRes);
  308.                         if (stRec != NIL)
  309.                             ReleaseResource((Handle) stRec);
  310.                     }    
  311.                     tHeight = TEGetHeight(0,32767,theText) -
  312.                                  (itemBox.bottom - itemBox.top);
  313.                     if (tHeight < 0)
  314.                         tHeight = 0;
  315.                     if (tdBar != NIL)
  316.                     {
  317.                         SetCtlMax(tdBar,tHeight);
  318.                         SetCRefCon(tdBar,(long) temp);
  319.                     }
  320.                     
  321.                     if (canWrite)
  322.                     {
  323.                         TEAutoView(TRUE,theText);
  324.                         TEActivate(theText);
  325.                     }
  326.                 }
  327.                 
  328.                 TextFont(saveFont);
  329.                 TextSize(saveSize);
  330.                 TextFace(saveFace);
  331.             }
  332.             SetPort(savePort);
  333.         }
  334.     }
  335.     return(theDialog);
  336. }
  337.  
  338.  
  339. pascal    void DisposeTDDialog(DialogPtr theDialog)
  340. {
  341.     /* disposes of dialog and all internal structures created */
  342.     
  343.     TDRecHdl        temp;
  344.     TEHandle        theText;
  345.     ControlHandle    tdBar;
  346.     
  347.     if (theDialog != NIL)
  348.     {
  349.         temp = (TDRecHdl) GetWRefCon(theDialog);
  350.         if (temp != NIL)
  351.         {
  352.             theText = (*temp)->TDText;
  353.             tdBar = (*temp)->TDControl;
  354.             
  355.             if (theText != NIL)
  356.                 TEDispose(theText);
  357.  
  358.             if (tdBar != NIL)
  359.                 DisposeControl(tdBar);
  360.  
  361.             DisposHandle((Handle)temp);
  362.         }
  363.         DisposDialog(theDialog);
  364.     }
  365. }    
  366.  
  367.  
  368. pascal    void ModalTDDialog(short *theItem)
  369. {
  370.     ModalFilterUPP    gTDFilterUPP;
  371.     
  372.     gTDFilterUPP = NewModalFilterProc((ProcPtr) TDFilter);
  373.     ModalDialog(gTDFilterUPP,theItem);
  374.     DisposeRoutineDescriptor(gTDFilterUPP);
  375. }
  376.  
  377.  
  378. pascal    short    ScrollTextDialog(short dialogID,short textUserItem,short textResourceID)
  379. {
  380.     /* high level alert-like call to display scrolly text dialog box */
  381.     
  382.     DialogPtr    theDialog;
  383.     Handle        itemHand;
  384.     Rect        itemBox;
  385.     short        theItem = 0,itemType;
  386.     GrafPtr        savePort;
  387.     
  388.     theDialog = GetTextDialog(dialogID,textUserItem,textResourceID,FALSE);
  389.     if (theDialog != NIL)
  390.     {
  391.         ShowWindow(theDialog);
  392.         SelectWindow(theDialog);
  393.         
  394.         GetDItem(theDialog,ok,&itemType,&itemHand,&itemBox);
  395.         InsetRect(&itemBox,-4,-4);
  396.         GetPort(&savePort);
  397.         SetPort(theDialog);
  398.         PenSize(3,3);
  399.         FrameRoundRect(&itemBox,16,16);
  400.         PenNormal();
  401.         SetPort(savePort);
  402.         
  403.         while (theItem == 0)
  404.             ModalTDDialog(&theItem);
  405.             
  406.         DisposeTDDialog(theDialog);
  407.     }
  408.     return(theItem);
  409. }
  410.  
  411.  
  412. pascal    void    TDSetText(DialogPtr theDialog,Ptr text,long tLength,StScrpHandle stInfo)
  413. {
  414.     /* sets the text in the TD dialog to be the given text. If you pass a style record
  415.         the text will be styled using that record. The scrollbar etc is recalculated
  416.         for the amount of text, and reset to the first line. Using this, multi-page
  417.         scrollable text can be implemented */
  418.         
  419.     TDRecHdl        temp;
  420.     TEHandle        theText;
  421.     ControlHandle    tdBar;
  422.     short                tHeight;
  423.     Rect            tdBarRect;
  424.     GrafPtr            savePort;
  425.     
  426.     if (theDialog != NIL)
  427.     {
  428.         temp = (TDRecHdl) GetWRefCon(theDialog);
  429.         
  430.         if (temp != NIL)
  431.         {
  432.             theText = (*temp)->TDText;
  433.             
  434.             if (theText != NIL)
  435.             {
  436.                 if ((*temp)->editable)
  437.                     TEDeactivate(theText);
  438.                     
  439.                 (*theText)->destRect = (*theText)->viewRect;
  440.                 TESetSelect(0,32767,theText);
  441.                 TEDelete(theText);
  442.                 GetPort(&savePort);
  443.                 SetPort(theDialog);
  444.                 
  445.                 if (stInfo == NIL)
  446.                 {
  447.                     TESetText(text,tLength,theText);
  448.                     InvalRect(&(*theText)->viewRect);
  449.                 }
  450.                 else
  451.                     TEStylInsert(text,tLength,stInfo,theText);
  452.                 
  453.                 TECalText(theText);
  454.                 tdBar = (*temp)->TDControl;
  455.                 SetPort(savePort);
  456.                 
  457.                 if (tdBar != NIL)
  458.                 {
  459.                     tdBarRect = (*tdBar)->contrlRect;
  460.                     InsetRect(&tdBarRect,0,3);
  461.                     tHeight = TEGetHeight(0,32767,theText) -
  462.                                 (tdBarRect.bottom - tdBarRect.top);
  463.                     if (tHeight < 0)
  464.                         tHeight = 0;
  465.                     SetCtlMax(tdBar,tHeight);
  466.                     SetCtlValue(tdBar,0);
  467.                 }
  468.                 if ((*temp)->editable)
  469.                     TEActivate(theText);
  470.                     
  471.             }
  472.         }
  473.     }
  474. }
  475.                 
  476.  
  477. pascal    void    TDSetResourceText(DialogPtr theDialog,short textResID)
  478. {
  479.     /* gets a 'TEXT' resource (& 'styl' if available) and calls TDSetText with it */
  480.     
  481.     Handle    text,style;
  482.     
  483.     text = GetResource('TEXT',textResID);
  484.     if (text != NIL)
  485.     {
  486.         style = GetResource('styl',textResID);
  487.         
  488.         HLock(text);
  489.         TDSetText(theDialog,*text,GetHandleSize(text),(StScrpHandle) style);
  490.         HUnlock(text);
  491.         
  492.         ReleaseResource(text);
  493.         if (style != NIL)
  494.             ReleaseResource(style);
  495.     }
  496. }    
  497.  
  498.  
  499. pascal    TEHandle    GetTDTextHdl(DialogPtr theDialog)
  500. {
  501.     /* utility to return text handle for direct manipulation */
  502.     
  503.     TDRecHdl    temp;
  504.     TEHandle    theText = NIL;
  505.     
  506.     if (theDialog != NIL)
  507.     {
  508.         temp = (TDRecHdl) GetWRefCon(theDialog);
  509.         if (temp != NIL)
  510.             theText = (*temp)->TDText;
  511.     }
  512.     return(theText);
  513. }    
  514.  
  515.  
  516. pascal    void    UpdateTDBar(TEHandle theText,ControlHandle theBar)
  517. {
  518.     /* calculates how much the text record is scrolled and updates the scrollbar. This is
  519.         done after an autoscroll operation */
  520.         
  521.     short        pOffset,tHeight;
  522.     Rect    tdBarRect;
  523.     
  524.     tdBarRect = (*theBar)->contrlRect;
  525.     InsetRect(&tdBarRect,0,3);
  526.     tHeight = TEGetHeight(0,32767,theText) -
  527.                 (tdBarRect.bottom - tdBarRect.top);
  528.     if (tHeight < 0)
  529.         tHeight = 0;
  530.     SetCtlMax(theBar,tHeight);
  531.     pOffset = (*theText)->viewRect.top - (*theText)->destRect.top;
  532.     SetCtlValue(theBar,pOffset);
  533. }                
  534.  
  535.  
  536. pascal    void    RecalTDBar(DialogPtr theDialog)
  537. {
  538.     /* recomputes parameters for the scroll bar. This is necessary if you change anything
  539.         during editing of the text */
  540.  
  541.     TDRecHdl    temp;
  542.         
  543.     if (theDialog != NIL)
  544.     {
  545.         temp = (TDRecHdl)GetWRefCon(theDialog);
  546.         if (temp != NIL)
  547.             UpdateTDBar((*temp)->TDText,(*temp)->TDControl);
  548.     }
  549. }
  550.  
  551.  
  552. /*----------------------------------------------------------------------------------------
  553.     Support added for modeless dialog 10/2/94
  554. ----------------------------------------------------------------------------------------*/
  555.  
  556. pascal Boolean     ModelessText(DialogPtr theDialog,short theItem,EventRecord *theEvent)
  557. {
  558.     /* handles the modeless dialog case. This function should be called when DIALOGSELECT
  559.         returns a value of TRUE, for a dialog containing a text panel. This handles the
  560.         hits in the panel as for the modal case, and editing if that is enabled. If some
  561.         other item in the dialog is hit, then the function returns FALSE and you must then
  562.         handle it yourself. WARNING!!! The dialog passed MUST be a scrolly-text type. It is
  563.         your responsibility to check first.
  564.     */
  565.     
  566.     if (theDialog != NIL)
  567.     {
  568.         return(TDFilter(theDialog,theEvent,&theItem));
  569.     }
  570. }